//
//  Network.swift
//  ExtremeHealth
//
//  Created by Zhongkai Li 李忠凯 on 2018/9/17.
//  Copyright © 2018 GanDongKeJi. All rights reserved.
//

import Moya
import RxSwift
import Alamofire
import HandyJSON

typealias JSON = [String: Any]

enum APIEnvironment {
    case stg
    case dev
    case prd
}

class DefaultAlamofireManager: Alamofire.SessionManager {
    static let sharedManager: DefaultAlamofireManager = {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
        configuration.timeoutIntervalForRequest = 20
        configuration.timeoutIntervalForResource = 20
        configuration.requestCachePolicy = .useProtocolCachePolicy
        return DefaultAlamofireManager(configuration: configuration)
    }()
}

struct Network {
    private static let provider = MoyaProvider<APIService>(manager: DefaultAlamofireManager.sharedManager,
                                                           plugins: [
                                                            NetworkLoggerPlugin(verbose: true),
                                                            RequestLoadingPlugin()])

    private static let environment: APIEnvironment = .stg
    private static let businessSuccessCode = "0000"
    private static let loginFailureCode = "E0001"
    private static let tokenFailureCode = "E0002"
    private static let resultDataKey = "content"
    private static let resultMsgKey = "resultMsg"
    private static let resultCodeKey = "resultCode"

    static func request<T: HandyJSON>(
        target: APIService,
        success successCallback: @escaping (T) -> Void,
        failure failureCallback: @escaping (MoyaError) -> Void,
        error   errorCallback:   @escaping (String) -> Void,
        dispose disposeBag: DisposeBag
        ) {

        provider.rx.request(target)
            .debug()
            .filterSuccessfulStatusCodes()
            .subscribe { (event) in
                switch event {
                case .success(let resp):
                    do {
                        let json = try resp.mapJSON()
                        if isSuccess(json) {
                            if let object = T.deserialize(from: tryGetContent(json)) {
                                successCallback(object)
                            } else {
                                debugPrint("json to model error")
                            }
                        } else if isLoginFailure(json) {
                            reLogin()
                        } else if let resultMsg = tryGetFailureMsg(json) {
                            errorCallback(resultMsg)
                        } else {
                            errorCallback("系统出错了，请稍后重试")
                        }
                    } catch let error {
                        if let moyaError = error as? MoyaError {
                            failureCallback(moyaError)
                        } else {
                            print(error)
                        }
                    }
                case .error(let error as MoyaError):
                    failureCallback(error)
                case .error:
                    assertionFailure("unexpected happend")
                }
            }.disposed(by: disposeBag)
    }

    static func isSuccess(_ json: Any) -> Bool {
        return ((json as? JSON)?[resultCodeKey] as? String) == businessSuccessCode
    }
    static func isLoginFailure(_ json: Any) -> Bool {
        return ((json as? JSON)?[resultCodeKey] as? String) == loginFailureCode
            || ((json as? JSON)?[resultCodeKey] as? String) == tokenFailureCode
    }
    static func tryGetFailureMsg(_ json: Any) -> String? {
        return (json as? JSON)?[resultMsgKey] as? String
    }
    static func tryGetContent(_ json: Any) -> JSON? {
        return (json as? JSON)?[resultDataKey] as? JSON
    }

    static func reLogin() {
        MainCoordinator.shared.loginHome()
    }
}
